package zdream.rockchronicle.core.graphics;

import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;

import java.lang.reflect.Constructor;
import java.util.Objects;
import java.util.function.BooleanSupplier;

import zdream.control.assets.FoeDescription;
import zdream.control.graphics.AbstractLevelGraphics;
import zdream.control.graphics.IPainter;
import zdream.control.profile.mm.MegaManProfile;
import zdream.control.region.Region;
import zdream.control.world.LevelCamera;
import zdream.control.world.MFoe;
import zdream.control.world.MHookType;
import zdream.control.world.MHooks;
import zdream.control.world.Ticker;
import zdream.rockchronicle.RockChronicle;
import zdream.rockchronicle.profiles.megaman.MegaManHealthMiscPainter;
import zdream.rockchronicle.utils.FilePathUtil;

/**
 * 处理镜头、绘制等
 * @author Zdream
 * @date (created) 2021-09-09   (last modified) 2021-09-09
 */
public class LevelGraphics extends AbstractLevelGraphics {
	public final RockChronicle app;

	public LevelGraphics(RockChronicle app) {
		this.app = Objects.requireNonNull(app);
	}

	public void init() {
		batch = new SpriteBatch();
		uiBatch = new SpriteBatch();
		initFont();

		camera.viewportWidth = Region.BLOCK_WIDTH;
		camera.viewportHeight = Region.BLOCK_HEIGHT;
		camera.position.set(camera.viewportWidth / 2.0f, camera.viewportHeight / 2.0f, 0);
	}

	protected void clearCanvas() {
		Gdx.gl.glClearColor(0.1f, 0, 0.2f, 1);
		Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
	}

	public SpriteBatch batch;

	/**
	 * 在界面启动时, 和每帧
	 * 修正 MapRender 渲染的瓦片地图的位置
	 */
	private void fixMapRender() {
		LevelCamera levelCamera = app.levels.camera;
		camera.position.x = levelCamera.view.centerX();
		camera.position.y = levelCamera.view.centerY();
		camera.update();

		// batch 相对于 room 的, 左下角就是 (0, 0)
		batch.setProjectionMatrix(camera.combined);
	}

	public void enableAndPrepare() {
		enabled = true;
		registerWorldPainter();
		prepareWorldTmxPainter();
		prepareProfilePainter();
	}

	/* **********
	 * 世界绘制 *
	 ********** */
	/**
	 * 原来的 levelCamera 是放在 {@link zdream.control.world.LevelCamera} 里面的.
	 * 但是现在把它提到这里
	 */
	public OrthographicCamera camera = new OrthographicCamera();
	public BooleanSupplier[] bindCleaner;

	@Override
	@SuppressWarnings("unchecked")
	protected IPainter createPainterByDescription(MFoe foe, FoeDescription description) {
		FoeDescription.Painter painterDesc = description.painter;
		if (painterDesc == null) {
			return null;
		}

		if (painterDesc.className != null) {
			String className = painterDesc.className;
			Class<?> painterClass;
			try {
				painterClass = Class.forName(className);
			} catch (ClassNotFoundException e) {
				throw new IllegalArgumentException(String.format("Painter class: %s 不存在", className), e);
			}

			boolean assignableFromFoe = IPainter.class.isAssignableFrom(painterClass);
			if (!assignableFromFoe) {
				throw new IllegalArgumentException(String.format("class: %s 不是 IPainter 的实现类", className));
			}

			Class<? extends IPainter> castClass = (Class<? extends IPainter>) painterClass;
			Constructor<?>[] constructors = castClass.getDeclaredConstructors();
			Constructor<?> selected = null;
			for (Constructor<?> constructor : constructors) {
				Class<?>[] parameterTypes = constructor.getParameterTypes();
				if (parameterTypes.length != 1) {
					continue;
				}
				if (!foe.getClass().isAssignableFrom(parameterTypes[0])) {
					continue;
				}
				selected = constructor;
				break;
			}
			if (selected == null) {
				throw new IllegalArgumentException(String.format("没有在 class: %s 中找到合适的构造方法", className));
			}

			try {
				IPainter painter = (IPainter) selected.newInstance(foe);
				if (painter instanceof SpritePainter) {
					((SpritePainter) painter).initTextures(app.assets);
				}
				return painter;
			} catch (Exception e) {
				throw new IllegalArgumentException(
						String.format("使用 %s 构建 class: %s 失败", selected, className), e);
			}
		}

		FoePainter.SpritePainterFactory factory = FoePainter.builderForWorld(foe);
		if (painterDesc.z > 0) {
			factory.setZIndex(painterDesc.z);
		}
		if (painterDesc.flashWhenAgeBiggerThan > 0) {
			factory.flashWhenAgeBiggerThan(painterDesc.flashWhenAgeBiggerThan);
		}

		try {
			final FoePainter foePainter = factory.create();
			foePainter.initTextures(app.assets);
			return foePainter;
		} catch (RuntimeException e) {
			throw new RuntimeException("创建 " + foe + " 的 FoePainter 失败", e);
		}
	}

	@Override
	protected void drawWorld(Ticker time) {
		fixMapRender();
		super.drawWorld(time);
	}

	private void registerWorldPainter() {
		if (bindCleaner != null) {
			for (BooleanSupplier supplier : bindCleaner) {
				supplier.getAsBoolean();
			}
		}

		bindCleaner = new BooleanSupplier[] {
			app.createSubscriber(MHookType.FOE_ADDED)
					.handle(this::onFoeAdded)
					.buildAndSave(),
		};
	}

	private void onFoeAdded(MHooks.MHookContext rawContext) {
		MHooks.FoeHookContext context = rawContext.as();
		BooleanSupplier cleaner = this.addWorldPainter(context.foe);
		context.foe.onClean(foe -> cleaner.getAsBoolean());
	}

	private void prepareWorldTmxPainter() {
		this.addWorldPainter(regionPainter = new RegionPainter(app));
		this.addUIPainter(foreplayPainter = new ForeplayPainter(app));
		this.addWorldPainter(shapePainter = new ShapePainter(app)); // for debug
	}

	/* **********
	 * TMX 绘制 *
	 ********** */

	RegionPainter regionPainter;
	ForeplayPainter foreplayPainter;
	ShapePainter shapePainter;

	/* **********
	 * UI 绘制  *
	 ********** */
	public SpriteBatch uiBatch;

	private void prepareProfilePainter() {
		System.out.printf("TODO 正常情况要通过 %s 来找对应的 Misc Painter%n", app.levels.profile1);
		this.uiPainters.add(new MegaManHealthMiscPainter((MegaManProfile) app.levels.profile1));
	}

	/* **********
	 *   文本   *
	 ********** */
	public BitmapFont font16;
	public BitmapFont font24;
	public BitmapFont fontPressStart24; // 仅英文

	private void initFont() {
		String text = FreeTypeFontGenerator.DEFAULT_CHARS + app.chineseCharacter();

		FreeTypeFontGenerator generator = new FreeTypeFontGenerator(FilePathUtil.localFiles("res", "font", "msyhbd.ttc"));
		FreeTypeFontGenerator.FreeTypeFontParameter parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
		parameter.size = 16;
		parameter.characters = text;
		font16 = generator.generateFont(parameter);
		generator.dispose();

		generator = new FreeTypeFontGenerator(FilePathUtil.localFiles("res", "font", "msyhbd.ttc"));
		parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
		parameter.size = 24;
		parameter.characters = text;
		font24 = generator.generateFont(parameter);
		generator.dispose();

		generator = new FreeTypeFontGenerator(FilePathUtil.localFiles("res", "font", "PressStart.ttf"));
		parameter = new FreeTypeFontGenerator.FreeTypeFontParameter();
		parameter.size = 24;
		parameter.characters = FreeTypeFontGenerator.DEFAULT_CHARS;
		fontPressStart24 = generator.generateFont(parameter);
		generator.dispose();
	}
	
}
